package com.lrs.s2sh.service.impl;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;

import com.lrs.s2sh.dao.GenericDao;
import com.lrs.s2sh.model.Person;
import com.lrs.s2sh.service.FlexiblePageService;
import com.lrs.s2sh.utils.StringUtils;
import com.lrs.s2sh.web.jsonUtils.MixBean;
import com.lrs.s2sh.xsd.jqGrid.cfgItems.JSONManipulateCfg;
import com.lrs.s2sh.xsd.jqGrid.cfgItems.JqGridColumn;
import com.lrs.s2sh.xsd.jqGrid.cfgItems.JqGridConfiguration;
import com.lrs.s2sh.xsd.schemaSource.AccordionItemType;
import com.lrs.s2sh.xsd.schemaSource.ActionDefinitionType;
import com.lrs.s2sh.xsd.schemaSource.ButtonItemType;
import com.lrs.s2sh.xsd.schemaSource.CategoryType;
import com.lrs.s2sh.xsd.schemaSource.ColumnEditType;
import com.lrs.s2sh.xsd.schemaSource.ColumnSearchType;
import com.lrs.s2sh.xsd.schemaSource.CustomizablePageType;
import com.lrs.s2sh.xsd.schemaSource.DataFieldDisplayType;
import com.lrs.s2sh.xsd.schemaSource.DataFilterType;
import com.lrs.s2sh.xsd.schemaSource.FieldDefinitionType;
import com.lrs.s2sh.xsd.schemaSource.PageDataType;
import com.lrs.s2sh.xsd.template.FlexiblePageFactory;
import com.lrs.s2sh.xsd.util.FlexibleConstants;
import com.lrs.s2sh.xsd.util.FlexiblePageLookupUtils;
import com.lrs.s2sh.xsd.wrapper.EEPageCfgWrapper;
import com.lrs.s2sh.xsd.wrapper.EEPageDataCfgWrapper;

@Service
public class FlexiblePageServiceImpl extends GenericManagerImpl<Person, Long> implements FlexiblePageService {

	private static final Logger log = Logger.getLogger(FlexiblePageServiceImpl.class);
	private static Map<Long, EEPageCfgWrapper> customizedEEMap = new HashMap<Long, EEPageCfgWrapper>();
    private static EEPageDataCfgWrapper eeSysCfgWrapper = null;
	
	@Override
	public GenericDao<Person, Long> getSearchImpl() {
		return null;
	}
    
	private long getPrincipalId() {
		String username = "sa";
		
		return 0;
	}
	
	@Override
	public EEPageCfgWrapper getEECustomizedWrapper() throws Exception {
		long pageType = FlexibleConstants.PAGE_TYPE_EE_CFG;
		long cfgId = getCfgIdInternal(getPrincipalId(), pageType);
		return getEECustomizedWrapperInternal(cfgId, pageType);
	}
	
	public EEPageCfgWrapper getEECustomizedWrapperByCfgId(long cfgId) throws Exception {
		long pageType = FlexibleConstants.PAGE_TYPE_EE_CFG;
		return getEECustomizedWrapperInternal(cfgId, pageType);
	}
	
	private long getCfgIdInternal(long pricipalId, long pageType) {
		long cfgId = 0;
		try {
//			cfgId =  pagePM.findPageCfgByPricAndPageType(pricipalId, pageType).getId();
		} catch(Exception e) {
		}
		
		return cfgId;
	}
	
	private EEPageCfgWrapper getEECustomizedWrapperInternal(long cfgId, long pageType) throws Exception {
		
		if (customizedEEMap.containsKey(cfgId)) {
			return customizedEEMap.get(cfgId);
		}
		
//		EEPageCfgWrapper cusWrapper = (EEPageCfgWrapper)customizedEECache.get(cfgId);
		
		// build flexible System configuration
		EEPageDataCfgWrapper dataWrapper = getEEDataCfgWrapperInternal();
		EEPageCfgWrapper cusWrapper = null;
		
		//build Cache
//		if(cusWrapper == null || (cfgId != 0 && !checkCacheVersion(cfgId, cusWrapper.getVersion()))) {
		if(cusWrapper == null) {
			
			// build flexible System configuration
			JAXBElement<?> tplElement = getJAXBElementByInternal(cfgId,
					FlexibleConstants.PAGE_TYPE_EE_CFG, FlexibleConstants.FLEXIBLE_EE_PACKAGE);
			CustomizablePageType pageCfg = (CustomizablePageType) tplElement.getValue();
			
			//init version of XMLcfgWrapper
			long version = 0L;
			if(cfgId != 0) {
//				version = pagePM.findPageCfg(cfgId).getVersion();
			}
			
			cusWrapper = new EEPageCfgWrapper(cfgId, version, pageCfg);
			
			buildFixedDataGridCfg(cusWrapper, dataWrapper);
			buildActionCfg(cusWrapper, dataWrapper);
			
			
			if (cusWrapper != null) {
				customizedEEMap.put(cfgId, cusWrapper);
			}
//			customizedEECache.insert(cfgId, cusWrapper);
		} 
		
		buildDynChangedCfg(cusWrapper, dataWrapper);
		
		return cusWrapper;
	}
	
	private boolean checkCacheVersion(long cfgId, long version) throws Exception {
//		Long latestVersion = pagePM.findPageCfg(cfgId).getVersion();
//		
//		return latestVersion.longValue() == version;
		return true;
	}

	private void buildActionCfg(EEPageCfgWrapper cusWrapper,EEPageDataCfgWrapper dataWrapper) {
		Map<String, ActionDefinitionType> sysActions = dataWrapper.getEEActionByPageId(FlexibleConstants.FLEXIBLE_EE_NAME);
		
		List<ButtonItemType> buttonItems = cusWrapper.getButtonItems();

		List<ActionDefinitionType> actionDefs = new ArrayList<ActionDefinitionType>();
		for (Iterator<ButtonItemType> iterator = buttonItems.iterator(); iterator.hasNext();) {
			ButtonItemType buttonItemType = iterator.next();
			
			ActionDefinitionType actionDef = sysActions.get(buttonItemType.getActionRef());
			
			//if the actionRef is not present in Sys Data, return 
			if(actionDef == null) {
				continue ;
			}
			
			//set action properties
			actionDef.setDesc(buttonItemType.getDesc() != null ? buttonItemType.getDesc() : actionDef.getDesc());
			actionDef.setIcon(buttonItemType.getIcon() != null ? buttonItemType.getIcon() : actionDef.getIcon());
			actionDef.setOrder(buttonItemType.getOrder());
			
			actionDefs.add(actionDef);
		}
		
		cusWrapper.setActionBtns(actionDefs);
		
	}

	
	private void buildFixedDataGridCfg(EEPageCfgWrapper cusWrapper, EEPageDataCfgWrapper dataWrapper) {
		Map<String, FieldDefinitionType> sysDataFields = dataWrapper.getEEFieldByPageId(FlexibleConstants.FLEXIBLE_EE_NAME);

		List<DataFieldDisplayType> dataFields = cusWrapper.getDataFields();
		Map<String, DataFieldDisplayType> dataFieldMap = cusWrapper.getDataFieldMap();
		
		Map<String, JqGridColumn> colModelMap = new HashMap<String, JqGridColumn>();
		List<JqGridColumn> colModel = new ArrayList<JqGridColumn>();
		String[] colNames = new String[sysDataFields.size()];
		
		int curIndex = 0;
		for (Iterator<DataFieldDisplayType> iterator = dataFields.iterator(); iterator.hasNext();) {
			DataFieldDisplayType dataField = iterator.next();
			
			FieldDefinitionType fieldDef = sysDataFields.get(dataField.getFieldNameRef());
			if(fieldDef == null) {
				dataFieldMap.remove(dataField.getFieldNameRef());
				iterator.remove();
				continue;
			}
			
			//client configuration
			colNames[curIndex++] = dataField.getDisplayName() == null ? fieldDef.getFieldLabel() : dataField.getDisplayName();
			JqGridColumn col = buildJqGridColumn(dataField, fieldDef);
			
			colModel.add(col);
			colModelMap.put(col.getName(), col);
			
		}
		
		//update user defined Configuration when System Upgrade
		if(dataFields.size() < sysDataFields.size()) {
			int index = dataFields.size();
			
			for (Iterator<Entry<String, FieldDefinitionType>> iterator = sysDataFields.entrySet().iterator(); iterator.hasNext();) {
				Entry<String, FieldDefinitionType> entry = iterator.next();
				
				if(dataFieldMap.containsKey(entry.getKey())) {
					continue;
				}
				
				FieldDefinitionType fieldDef = entry.getValue();
				
				DataFieldDisplayType dataField = new DataFieldDisplayType();
				dataField.setFieldNameRef(fieldDef.getId());
				dataField.setHidden(true);
				
				dataFields.add(dataField);
				dataFieldMap.put(dataField.getFieldNameRef(), dataField);
				
				colNames[index++] = dataField.getDisplayName() == null ? fieldDef.getFieldLabel() : dataField.getDisplayName();
				JqGridColumn col = buildJqGridColumn(dataField, fieldDef);
				
				colModel.add(col);
				colModelMap.put(col.getName(), col);

			}
			
		}
		
		JqGridConfiguration jqGridCfg = new JqGridConfiguration();
		buildJqGridDefaultCfg(jqGridCfg);

		jqGridCfg.setSortname(cusWrapper.getDataGridSortName());
		jqGridCfg.setSortorder(cusWrapper.getDataGridSortOrder());
		
		jqGridCfg.setColNames(colNames);
		jqGridCfg.setColModel(colModel);
		
		cusWrapper.setColModelMap(colModelMap);
		cusWrapper.setJqGridCfg(jqGridCfg);
		
	}
	
	private JqGridColumn buildJqGridColumn(DataFieldDisplayType dataField, FieldDefinitionType fieldDef) {
		JqGridColumn col = new JqGridColumn();
		
		//set hiddendlg column
		col.setHidedlg(fieldDef.isHiddenDlg() != null ? fieldDef.isHiddenDlg() : FlexibleConstants.FLEXIBLE_EE_COL_HIDDENDLG_DEFAULT);
		
		//set sortable column 
		col.setSortable(FlexibleConstants.FLEXIBLE_EE_COL_SORTABLE_DEFAULT);
		if(fieldDef.getSortName() != null) {
			col.setIndex(fieldDef.getSortName());
			col.setSortable(true);
			
			if(dataField.isSortable() != null) {
				col.setSortable(dataField.isSortable());
			}
		}
		
		//set seachable column
		if(fieldDef.isSearchable() != null && fieldDef.isSearchable()) {
			col.setSearch(dataField.isSearchable() == null ? FlexibleConstants.FLEXIBLE_EE_COL_SEARCHABLE_DEFAULT : dataField.isSearchable());
			
			///{value:{0:' ',65:'Pending Review'}}
			ColumnSearchType searchType = fieldDef.getSearchType();
			switch (searchType) {
				case SELECT:
					col.setStype(searchType.value().toLowerCase());
					
					//check whether is fixed lookup data
					if(fieldDef.isFixedSearchLookup() == null ||  fieldDef.isFixedSearchLookup()) {
						col.setSearchoptions(getSelectSearchOptions(fieldDef.getSearchLookupRef()));
					}
					break;
				case TEXT:
					col.setStype(ColumnSearchType.TEXT.value().toLowerCase());
					
					break;
				case DATE:
					col.setStype(ColumnSearchType.TEXT.value().toLowerCase());
					col.setSearchoptions(getSearchOptions("datePick"));
					break;
				case AJAX:
					col.setStype(ColumnSearchType.TEXT.value().toLowerCase());
					
					String ajaxFunName = fieldDef.getCustomSearchFun() != null ? fieldDef.getCustomSearchFun() : FlexibleConstants.FLEXIBLE_EE_DEFAULT_AJAX_FUN;
					col.setSearchoptions(getSearchOptions(ajaxFunName));
					
					break;
				default:
					col.setStype(ColumnSearchType.TEXT.value().toLowerCase());
			}
			
		} else {
			col.setSearch(FlexibleConstants.FLEXIBLE_EE_COL_SEARCHABLE_DEFAULT);
		}
		
		col.setName(fieldDef.getId());
		col.setLabel(fieldDef.getFieldLabel());
		if(fieldDef.getCustomFormatterType() != null) {
			MixBean formatter = new MixBean(fieldDef.getCustomFormatterType(), true);
			col.setFormatter(formatter);
		} else {
			if(fieldDef.getFormatterType() != null) {
				MixBean formatter = new MixBean(fieldDef.getFormatterType().value().toLowerCase(), false);
				col.setFormatter(formatter);
			}
		}
		
		col.setEditable(FlexibleConstants.FLEXIBLE_EE_COL_EDITABLE_DEFAULT);
		
		if(fieldDef.getEditType() != null) {
			col.setEdittype(fieldDef.getEditType().value().toLowerCase());
			
			Map editoptions = new HashMap();
			
			switch(fieldDef.getEditType()) {
			case CHECK_BOX: 
				editoptions.put("value", "true:false");
				break;
				
			case SELECT: 
				editoptions = getEditOptions(fieldDef.getEditLookupRef());
				break;
				
			case CUSTOM:
				String customFun = fieldDef.getCustomEditFun();
				String customCallBack = fieldDef.getCustomEditCallBack();
				
				if(StringUtils.isNotNullAndNotEmpty(customFun)) {
					editoptions.put(FlexibleConstants.FLEXIBLE_CUSTOM_ELEM, new MixBean(customFun, true));
				}
				
				if(StringUtils.isNotNullAndNotEmpty(customCallBack)) {
					editoptions.put(FlexibleConstants.FLEXIBLE_CUSTOM_VALUE, new MixBean(customCallBack, true));
				}
				
				if(StringUtils.isNotNullAndNotEmpty(fieldDef.getEditLookupRef())) {
					editoptions.put("value", getLookupOptions(fieldDef.getEditLookupRef()));
				}
				
				break;
			}
			if(fieldDef.getFormatterType() != null && fieldDef.getFormatterType().value().equalsIgnoreCase("checkbox")) {
				editoptions.put("value", "true:false");
			}
			
			col.setEditoptions(editoptions);
			
		} else {
			col.setEdittype(ColumnEditType.TEXT.value().toLowerCase());
		}
		
		col.setWidth(dataField.getWidth() == null ? FlexibleConstants.FLEXIBLE_EE_COL_WIDTH_DEFAULT : dataField.getWidth().intValue());
		col.setAlign(dataField.getAlign() == null ? FlexibleConstants.FLEXIBLE_EE_COL_ALIGN_DEFAULT : dataField.getAlign().value());
		col.setResizable(FlexibleConstants.FLEXIBLE_EE_COL_RESIZABLE_DEFAULT);
		col.setHidden(dataField.isHidden() == null ? FlexibleConstants.FLEXIBLE_EE_COL_HIDDEN_DEFAULT : dataField.isHidden());

		return col;
	}
	
	private JqGridConfiguration buildJqGridDefaultCfg(JqGridConfiguration jqGridCfg) {
		jqGridCfg.setDatatype(FlexibleConstants.FLEXIBLE_EE_GRID_DATATYPE_DEFAULT);
		jqGridCfg.setUrl(FlexibleConstants.FLEXIBLE_EE_COL_URL_DEFAULT);
		jqGridCfg.setHeadertitles(FlexibleConstants.FLEXIBLE_EE_GRID_SHOW_HEADERS);
		
		JSONManipulateCfg jsonReader = new JSONManipulateCfg();
		jsonReader.setId("workflowId");
		jsonReader.setPage(FlexibleConstants.FLEXIBLE_PAGER_PAGE);
		jsonReader.setTotal(FlexibleConstants.FLEXIBLE_PAGER_TOTAL );
		jsonReader.setRecords(FlexibleConstants.FLEXIBLE_PAGER_RECORDS);
		jsonReader.setRoot(FlexibleConstants.FLEXIBLE_PAGER_ITEMS);
		jsonReader.setRepeatitems(false);

		jqGridCfg.setJsonReader(jsonReader);
		
		jqGridCfg.setAutowidth(true);
		jqGridCfg.setShrinkToFit(false);
		jqGridCfg.setHeight(FlexibleConstants.FLEXIBLE_EE_GRID_HEIGHT);
		
		jqGridCfg.setViewrecords(FlexibleConstants.FLEXIBLE_EE_COL_VIEWRECORD_DEFAULT);
		jqGridCfg.setCellEdit(FlexibleConstants.FLEXIBLE_EE_COL_CELLEDIT_DEFAULT);
		jqGridCfg.setCellsubmit(FlexibleConstants.FLEXIBLE_EE_COL_CELLSUBMIT_DEFAULT);
		jqGridCfg.setMultiselect(FlexibleConstants.FLEXIBLE_EE_COL_MULTIPALSELECT_DEFAULT);
		jqGridCfg.setSortable(FlexibleConstants.FLEXIBLE_EE_COL_SORTABLE_DEFAULT);

		
		jqGridCfg.setPager(FlexibleConstants.FLEXIBLE_EE_PAGER_NAME);
		jqGridCfg.setRowNum(20);
		jqGridCfg.setRowList(new int[] {10,20,30});
//		jqGridCfg.setToolbar(new String[]{"true", "top"});
		
		return jqGridCfg;
	}
	
	private void buildDynChangedCfg(EEPageCfgWrapper cusWrapper, EEPageDataCfgWrapper dataWrapper) {
		Map<String, FieldDefinitionType> sysDataFields = dataWrapper.getEEFieldByPageId(FlexibleConstants.FLEXIBLE_EE_NAME);

//		List<DataFieldDisplayType> dataFields = cusWrapper.getDataFields();
		
		for (Iterator<Entry<String, FieldDefinitionType>> iterator = sysDataFields.entrySet().iterator(); iterator.hasNext();) {
			
			Entry<String, FieldDefinitionType> entry = iterator.next();
			FieldDefinitionType fieldDef = entry.getValue();
			
			//set editable column according to privilege
			if(fieldDef.isReadOnly()!=null && !fieldDef.isReadOnly()) {
				JqGridColumn column = cusWrapper.getColModelMap().get(fieldDef.getId());
				
				boolean hasPrivilege = true;
				String editPrivilege = fieldDef.getEditPrivileges();
				
				if(editPrivilege != null) {
					String privileges[] = editPrivilege.split(",");
					for(int j=0; j<privileges.length; j++) {
						try {
							hasPrivilege = true;
//							hasPrivilege = psr.doIHavePrivilege(privileges[j]);
							if(!hasPrivilege) break;
						} catch (Exception e) {}
					}
				}
				column.setEditable(hasPrivilege);
			}
			
			//set not fixed search lookup data
			if(fieldDef.isSearchable() != null && fieldDef.isSearchable() && fieldDef.isFixedSearchLookup() != null && !fieldDef.isFixedSearchLookup()) {
				
				ColumnSearchType searchType = fieldDef.getSearchType();
				switch (searchType) {
					case SELECT:
						JqGridColumn column = cusWrapper.getColModelMap().get(fieldDef.getId());
						column.setStype(searchType.value().toLowerCase());
						
						column.setSearchoptions(getSelectSearchOptions(fieldDef.getSearchLookupRef()));
						break;
				}
			}
			
		}
		
	}

	private Map getSelectSearchOptions(String methodName) {
		Map searchoptions = new HashMap();
		
		Map value = new LinkedHashMap();
		value.put("0", " ");
		value.putAll(getLookupOptions(methodName));
		
		searchoptions.put("value", value);
		
		return searchoptions;
	}
	
	private Map getSearchOptions(String funName) {
		Map searchoptions = new HashMap();
		
		searchoptions.put("dataInit", new MixBean(funName, true));
		
		return searchoptions;
	}
	
	private Map getEditOptions(String methodName) {
		
		Map editOptions = new HashMap();
		editOptions.put("value", getLookupOptions(methodName));
		
		return editOptions;
	}
	
	private Map getLookupOptions(String methodName) {
		Map map = null;
		try {
			Method method = FlexiblePageLookupUtils.class.getMethod(methodName, new Class[0]);
			map = (Map)method.invoke(null, new Object[0]);
		} catch (Exception e) {
			log.error("can't find lookup name = " + methodName + " in FlexiblePageLookupUtils class" + e);
		}
		return map == null ? new HashMap() : map; 
	}

	private EEPageDataCfgWrapper getEEDataCfgWrapperInternal() throws Exception {
		if(eeSysCfgWrapper != null) {
			return eeSysCfgWrapper;
		}

		JAXBElement<?> tplElement = getJAXBElementByInternal(0,
				FlexibleConstants.PAGE_TYPE_EE_DATA, FlexibleConstants.FLEXIBLE_EE_PACKAGE);
		PageDataType pageDataCfg = (PageDataType) tplElement.getValue();
		eeSysCfgWrapper = new EEPageDataCfgWrapper(pageDataCfg);

		return eeSysCfgWrapper;
		
	}
	
	private JAXBElement<?> getJAXBElementByInternal(long cfgId,
			long pageType, String packageName) throws Exception, JAXBException {
		InputStream tplInputStream;
		if(cfgId == 0) {
			tplInputStream = getSysPageCfgInputStream(pageType);
		} else {
			//byte[] templateContent = pagePM.getTemplateContent(cfgId);
			//tplInputStream = new ByteArrayInputStream(templateContent);
			
			
			tplInputStream = getSysPageCfgInputStream(pageType);
		}
		JAXBContext jc = JAXBContext.newInstance(packageName);
		Unmarshaller u = jc.createUnmarshaller();
		JAXBElement<?> tplElement = (JAXBElement<?>) u.unmarshal(tplInputStream);
		return tplElement;
	}
	
	private InputStream getSysPageCfgInputStream(long pageType) {
		
		InputStream tplInputStream = null;
		if(pageType == FlexibleConstants.PAGE_TYPE_EE_CFG) {
			tplInputStream = FlexiblePageFactory.class.getResourceAsStream(FlexibleConstants.FLEXIBLE_EE_XML_PACKAGE + "flexible-page.xml");
		} else if (pageType == FlexibleConstants.PAGE_TYPE_EE_DATA) {
			tplInputStream = FlexiblePageFactory.class.getResourceAsStream(FlexibleConstants.FLEXIBLE_EE_XML_PACKAGE + "flexible-page-data.xml");
		}
		return tplInputStream;
	}
	
	@Override
	public long createEECategory(long cfgId, CategoryType category) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public long modifyEECategory(long cfgId, CategoryType category) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public long removeEECategory(long cfgId, String categoryId) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public long createEECategoryItem(long cfgId, String categoryId,
			AccordionItemType item, DataFilterType dataFilter) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public long modifyEECategoryItem(long cfgId, AccordionItemType item) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public long modifyEECateoryItemPos(long cfgId, String[][] idArrs) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public long removeEECategoryItem(long cfgId, String categoryId,
			String itemId) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public long modifyEEDataFilter(long cfgId, DataFilterType item) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public long modifyEEDataGridPro(long cfgId, String[] hiddenIds,
			Integer[] orders) {
		// TODO Auto-generated method stub
		return 0;
	}
	
	

}
